#include "KMeans.h"
#include "SampleSpace.h"
#include "Toolkit.h"
#include "Distance.h"

#include <iostream>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cfloat>

KMeans::KMeans(SampleSpace &sampleSpace, int K, vector<Sample *> &init): Cluster(sampleSpace) {
    assert(K == (int)init.size());
    
    m_K = K;

    sampleSpace.newSampleArray(m_init, K);
    for (int i = 0; i < K; i++) {
        sampleSpace.copy(*m_init.at(i), *init.at(i));
    }
}

KMeans::~KMeans() {
    m_sampleSpace.deleteSampleArray(m_init);
}

void KMeans::cluster(vector<Sample *> &sampleList) {
    int size = sampleList.size();

    int dim = m_sampleSpace.getDimension();
    _Center **centers = new _Center*[m_K];
    for(int i = 0; i < m_K; i++) {
        centers[i] = new _Center(dim);
    }
    if (m_init.size() == 0) {
        srand((unsigned)time(NULL));

        int index;
        for(int i = 0; i < m_K; i++) {
            index = rand() % size;
            m_sampleSpace.copy(*centers[i], *sampleList.at(index));
        }
    } else {
        for(int i = 0; i < m_K; i++) {
            m_sampleSpace.copy(*centers[i], *m_init[i]);
        }
    }

    float min;
    float distance;
    bool finished = false;
    int label;
    
    while (!finished) {
        for (int i = 0; i < size; i++) {

            min = FLT_MAX;
            for(int j = 0; j < m_K; j++) {
                distance = m_sampleSpace.distance(*sampleList.at(i), *centers[j]);
                
                if(distance < min) {
                    min = distance;
                    label = j;
                }
            }
            sampleList.at(i)->setClass(label);

            centers[sampleList.at(i)->getClass()]->addSample(sampleList.at(i));
        }

        finished = true;
        for (int i = 0; i < m_K; i++) {
            finished &= !centers[i]->move2center();
            centers[i]->clearSample();
        }
    }

    for(int i = 0; i < m_K; i++) {
        delete centers[i];
    }
    delete []centers;
}

bool KMeans::_Center::move2center() {
    int dim = _Center::m_dim;
    float *feature = NULL;
    float *temp = new float[dim];
    memset(temp, 0, dim*sizeof(float));

    int size = m_sampleList.size();
    if(size == 0) {
        delete []temp;
        return false;
    }

    for(int i = 0; i < size; i++) {

        feature = m_sampleList.at(i)->getFeature();
        for (int j = 0; j < dim; j++) {
            temp[j] += feature[j]/size;
        }
    }

    int offset = 0;
    for( ; offset < dim && m_feature[offset] == temp[offset]; offset++) {
    }

    bool moved = true;
    if(offset == dim) {
        moved = false;
    } else {
        memcpy(m_feature, temp, dim*sizeof(float));
    }
    
    delete[] temp;

    return moved;
}
